package IEFYP.JunkCallFiltering;

import java.lang.reflect.Method;

import com.android.internal.telephony.ITelephony;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.media.AudioManager;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.TelephonyManager;
import android.text.format.Time;
import android.util.Log;
import android.widget.Toast;


/******
 * 1. Recover to the original settingV
 * 2. Priority problem( black -> user -> suspicious)V
 * 3. Cannot stop vibration.V
 * 4. How about three line case ???V
 * 5. performance about large number of data!!!!<!!!<!!<!<!<X
 * @author KOLee
 *
 */
public class CallReceiver extends BroadcastReceiver {

	private final int LimitDur = 20;
	private String number;
	SharedPreferences myPrefs;
	DBhelper b_helper;
	DBhelper u_helper;
    private ITelephony telephonyService;
    private TelephonyManager tm;
    private AudioManager am;
    private static boolean Bdone;
    private static boolean Sdone;
    private static boolean Udone;
    private static boolean NoCallNum;
    private static boolean afterIDLE;
    private static boolean afterAnswer;
    private static boolean afterRinging= false; 
    private static boolean anwserNendCall;
    private static boolean inContactlist;
    private static int defaultRingerMode = -1 ;
    private static int defaultVibrateMode = -1;
    private Context con;
    
    
    
	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
    	
		String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
		myPrefs= context.getSharedPreferences("ActionOnJunkCall", 0);
		
		am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);

		con = context;
		

		Log.v("default=",Integer.toString(defaultRingerMode));
		tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
		 connectToTelephonyService();
	
		u_helper = new DBhelper(context,"userlist");
		b_helper = new DBhelper(context,"blacklist");
		
		
		
		if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)){
			Bdone = false;
			Udone = false;
			Sdone = false;
			NoCallNum = false;
			afterIDLE = false;
			afterAnswer = false;
			afterRinging= true;
			anwserNendCall = false;
			defaultRingerMode = am.getRingerMode();
			defaultVibrateMode = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
			inContactlist = false;
			
			number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
			if(!contactExists(context,number)){ //if number exist in contact list, do nothing!!! // *****problem about no number displayed****
				if(myPrefs.getBoolean("blacklist", false)){
					b_helper.OpenDB();
					if( !b_helper.findNumber(number,true).equals("NULL")){
					ActOnCalls(myPrefs,"junkcall");
					Toast.makeText(context, "Junk Call (blacklist):"+number,Toast.LENGTH_LONG).show();  
					Bdone = true;
					}
					b_helper.close();							
				}
				
				if(myPrefs.getBoolean("userlist", false)&&!Bdone){
					u_helper.OpenDB();
					if(!u_helper.findNumber(number,false).equals("NULL")){
					ActOnCalls(myPrefs,"junkcall");
					Toast.makeText(context, "Junk Call (userlist):"+number,Toast.LENGTH_LONG).show();  
					Udone = true;
					}
					u_helper.close();
					
				} 
				if(number!= null){
					if(myPrefs.getBoolean("3.", false) && number.startsWith("3")&&!Bdone&&!Udone){
						ActOnCalls(myPrefs,"suspcall");
						Toast.makeText(context, "Suspicious Call(start with 3) ",Toast.LENGTH_LONG).show();  	
						 Sdone = true;
					}		
					else if(myPrefs.getBoolean("2.", false) && number.startsWith("2")&&!Bdone&&!Udone){
						ActOnCalls(myPrefs,"suspcall");
						Toast.makeText(context, "Suspicious Call (start with 2)",Toast.LENGTH_LONG).show();    						 
						 Sdone = true;
					}
				}else if(myPrefs.getBoolean("no_num", false) && number == null &&!Bdone&&!Udone){
					 ActOnCalls(myPrefs,"suspcall");
					Toast.makeText(context, "Suspicious Call (no number)",Toast.LENGTH_LONG).show();    					
					 NoCallNum = true;
					// Sdone = true; will not prompt if no number
				}
			}else{			
				inContactlist = true;
			}
			
		}else if(state.equals(TelephonyManager.EXTRA_STATE_IDLE)){ // afterRinging + idle = end call
			am.setRingerMode( defaultRingerMode);
			am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,defaultVibrateMode);
			afterIDLE = true;
			afterRinging = false;
		}else if(state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK ) && afterRinging){ // afterRinging + idle = anwser call
			if(anwserNendCall){ 
				//am.setRingerMode( defaultRingerMode);
				//am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,defaultVibrateMode);
				blockCall();
				afterAnswer = false;
				afterRinging = false;
				anwserNendCall = false;
			}else{
				//afterIDLE = false;
				am.setRingerMode( defaultRingerMode);
				am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,defaultVibrateMode);
				afterAnswer = true;
				afterRinging = false;
			}
		}
		Log.v("AfterIDLE = ", Boolean.toString(afterIDLE) );
		if(afterIDLE&&afterAnswer&&myPrefs.getBoolean("duration", false)&&!inContactlist&&!Bdone&&!Udone&&!NoCallNum){//the call duration measurement is for suspecious call only
			if(DurationIdentifier(context) && !myPrefs.getString("suspcall", "NULL").equals("No Actions")){	
				Log.v("into1","1");
				afterIDLE = false;
				afterAnswer = false;
				ReportPrompt.launch(context);	
			}
		}
		
		if(afterIDLE&&afterAnswer
				&&((Sdone && !myPrefs.getString("suspcall", "NULL").equals("No Actions")) || Udone && !myPrefs.getString("junkcall", "NULL").equals("No Actions") ) //(Bdone && !myPrefs.getString("junkcall", "NULL").equals("No Actions") && Sdone && !myPrefs.getString("suspcall", "NULL").equals("No Actions")) 
				&&!inContactlist) // all suspcious and blacklist call will asking for reporting
		{	Log.v("into1","2");
			afterIDLE = false;
			afterAnswer = false;
			Sdone = false;
			Udone = false;
			ReportPrompt.launch(context);
		}
	}
	
	private void ActOnCalls(SharedPreferences _myPrefs,String Target){
		String pref = _myPrefs.getString(Target, "NULL");
		Log.v("ActOnCalls",pref+" "+Target);
		if(pref.equals("Block/To VoiceMail")){
			silence();
			am.setRingerMode( AudioManager.RINGER_MODE_SILENT);
			blockCall();
			if(JunkCallKiller.serviceStart){
				
				Intent intent = new Intent(AutoReport.ACTION);
				intent.putExtra("type","block");				
				intent.putExtra("callno", number);				
				ReportJunkCall.MyDateTime cTime = new ReportJunkCall.MyDateTime();
				String ReportTime = cTime.currentTime;
				intent.putExtra("time", ReportTime);
				Log.v("Report time ", ReportTime);
				con.sendBroadcast(intent);
			}
			Log.v("Block/To VoiceMail",pref+" "+Target);
		}else if(pref.equals("Answer then Block")){
			Log.v("Answer then Block",pref+" "+Target);
			silence();
			am.setRingerMode( AudioManager.RINGER_MODE_SILENT);
			anwserNendCall();
			
			Intent intent = new Intent(AutoReport.ACTION);
			intent.putExtra("type","block");				
			intent.putExtra("callno", number);				
			ReportJunkCall.MyDateTime cTime = new ReportJunkCall.MyDateTime();
			String ReportTime = cTime.currentTime;
			intent.putExtra("time", ReportTime);
			Log.v("Report time ", ReportTime);
			con.sendBroadcast(intent);
			
		}else if(pref.equals("No Ringing")){	
			Log.v("No Ringing",pref+" "+Target);
			if(am.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)){//vibration only
				am.setRingerMode( AudioManager.RINGER_MODE_VIBRATE); 
			}else {//silent and no vibration
				silence();
				am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,AudioManager.VIBRATE_SETTING_OFF);
				am.setRingerMode( AudioManager.RINGER_MODE_SILENT);
			}
		}else if(pref.equals("No Ringing and Vibration")){
			Log.v("No Ringing and Vibration",pref+" "+Target);
			silence();
			am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,AudioManager.VIBRATE_SETTING_OFF); //ensure NO vibration
			am.setRingerMode( AudioManager.RINGER_MODE_SILENT);
		}else if(pref.equals("No Actions")){
			Log.v("No Actions",pref+" "+Target);
			// am.setRingerMode( defaultRingerMode);
		}

	}
	
	 private void silence(){
		    try
	          {
	                  telephonyService.silenceRinger();
	          }
	          catch (RemoteException e)
	          {
	                  e.printStackTrace();
	                  Log.e("call prompt","FATAL ERROR: call to service method endCall failed.");
	                  Log.e("call prompt","Exception object: "+e);
	          }
	 }
	  private void blockCall() {
          try
          {
                  telephonyService.silenceRinger();
                  telephonyService.endCall();                
          }
          catch (RemoteException e)
          {
                  e.printStackTrace();
                  Log.e("call prompt","FATAL ERROR: call to service method endCall failed.");
                  Log.e("call prompt","Exception object: "+e);
          }
	  }
	  
	  private void anwserNendCall() {
          try
          {
                  telephonyService.silenceRinger();
                  telephonyService.answerRingingCall();
                  anwserNendCall = true;
          }
          catch (RemoteException e)
          {
                  e.printStackTrace();
                  Log.e("call prompt","FATAL ERROR: call to service method endCall failed.");
                  Log.e("call prompt","Exception object: "+e);
          }
  }
	  
	    /** From Tedd's source
	     * http://code.google.com/p/teddsdroidtools/source/browse/
	     * get an instance of ITelephony to talk handle calls with
	     */
	    @SuppressWarnings("rawtypes")
		private void connectToTelephonyService() {
	            try
	            {
	            	   
	                    // "cheat" with Java reflection to gain access to TelephonyManager's ITelephony getter /***!!!!!****/
	                    Class c = Class.forName(tm.getClass().getName());
	                    Method m = c.getDeclaredMethod("getITelephony");
	                    m.setAccessible(true);
	                    telephonyService = (ITelephony)m.invoke(tm);

	            } catch (Exception e) {
	                    e.printStackTrace();
	                    Log.e("call prompt","FATAL ERROR: could not connect to telephony subsystem");
	                    Log.e("call prompt","Exception object: "+e);
	            }
	    }

/*reference: http://efreedom.com/Question/1-3505865/Android-Check-Phone-Number-Present-Contact-List-Phone-Number-Retreive-Phone-Call*/
	    public boolean contactExists(Context context, String number) {
	    	/// number is the phone number
	    	if(number!= null){
	    	Uri lookupUri = Uri.withAppendedPath(
	    	PhoneLookup.CONTENT_FILTER_URI, 
	    	Uri.encode(number));
	    	String[] Projection = { PhoneLookup._ID, PhoneLookup.NUMBER, PhoneLookup.DISPLAY_NAME };
	    	Cursor c = context.getContentResolver().query(
	    			lookupUri,
	    			Projection, 
	    			null, null, null);
	    	try {
	    	   if (c.moveToFirst()) {
	    	      return true;
	    	}
	    	} finally {
	    	if (c != null)
	    	   c.close();
	    	}
	    		return false;
	    	}else{
	    		return false;
	    	}
	    }
	        
	    public boolean DurationIdentifier(Context context) {
	    	String[] Projection = { android.provider.CallLog.Calls.DURATION,android.provider.CallLog.Calls.NUMBER,android.provider.CallLog.Calls.DATE};
			Cursor c = context.getContentResolver().query(
					android.provider.CallLog.Calls.CONTENT_URI,
					Projection,
					null,null,android.provider.CallLog.Calls.DATE+ " DESC");  // Can we find the most recent DATE directly???
			   
			if (c.moveToFirst()) {
					int DurationCol = c.getColumnIndex(android.provider.CallLog.Calls.DURATION);
					int NumberCol = c.getColumnIndex(android.provider.CallLog.Calls.NUMBER);
					Log.v("DurationIndentifier: NUMBER=",c.getString(NumberCol) );		
					Log.v("DurationIndentifier: Duration=", c.getString(DurationCol) );
					int Duration = Integer.parseInt(c.getString(DurationCol));			
					c.close();		
					if(Duration < LimitDur ){ // Duration > 0 must not be block&& Duration > 0
						Log.v("DurationIndentifier:true ", Integer.toString(Duration) );
		    	      return true;
					}else{
						Log.v("DurationIndentifier:false ", Integer.toString(Duration) );
						return false;
					}
		    	}else{
		    		Log.e("DurationIndentifier:", "Get null data from cursor!");
		    		return false;
		    	}	    	
	    }

}
